Lab 06 - Robot Operating System 2 - interactions through publishing
Robot Operating System 2 - interactions through publishing

1. Activity Identity
| Activity title | Introduction to Robotics |
|---|---|
| Topic | Robotics / ROS 2 / Simulation and Hardware |
| Authors | Institute of Robotics and Machine Intelligence Dominik Belter, Jakub Chudzinski, Marcin Czajka, Kamil Młodzikowski |
| Target learners | Bachelor (Computer Science / IT, Robotics) |
| Estimated duration | 1.5 hours |
| Difficulty level | Beginner |
| FOSSBot environment | Hybrid (Simulator and physical FOSSBot) |
| Licence | CC BY 4.0 |
2. Learning Objectives and Competences
| ID | Learning outcome | Related competences | Assessment evidence |
|---|---|---|---|
| LO1 | Students will be able to publish messages to a topic from the
command line with ros2 topic pub, controlling the robot’s
motion by setting the fields of a geometry_msgs/msg/Twist
message. |
Robotics middleware knowledge; sensor and actuator interfacing. | Screenshot of a ros2 topic pub command next to the
robot moving / /odom updating (Submission item 1). |
| LO2 | Students will be able to distinguish single (--once)
from continuous (--rate) publishing, stop a robot safely
with a zero-velocity command, and explain what happens when two
publishers share one topic. |
Computational thinking; reasoning about concurrency in a distributed system. | Screenshot of rqt_graph with two publishers on
/cmd_vel (Submission
item 2). |
| LO3 | Students will be able to apply the identical publishing workflow to a physical FOSSBot over a shared network and explain how its behaviour differs from the simulator. | Tool selection for working with a robot; operating real hardware safely over a network. | Photo or short video of the real robot moving, with the published command visible (Submission item 3). |
3. Prerequisites
You can build and start the FOSSBotEduSim container, you know what a node, topic and message are, and you have driven the simulated robot with
teleop_twist_keyboard.A Linux workstation with Docker, set up exactly as in Lab 05.
For the final step only: access to the lab Wi-Fi network and a physical FOSSBot, provided and powered on by your instructor.
Ability to capture evidence: screenshots, photos or a short video.
4. Required Material and Setup
| Category | Item | Version / Quantity | Notes |
|---|---|---|---|
| Hardware | Workstation | 1 per student | The same Docker-capable Linux PC used in Lab 05. See Step 1. |
| Software | FOSSBotEduSim simulator | latest from main branch |
Already cloned and built into the ros2_fossbot_edu
Docker image during Lab 05. If you no longer have the image, rebuild it
as described in Lab 05. |
| Hardware | Physical FOSSBot | 1 per group (final step only) | Instructor-provided and powered on. It exposes the same ROS 2 topics
as the simulator (/cmd_vel, /odom,
/scan). |
| Hardware | Lab Wi-Fi router / AP | 1 per room (final step only) | Instructor-provided. The robot and your workstation join the same network so they can discover each other. |
Tip: Every simulation step (1 to 5) works without the physical robot. If hardware is not available, complete those steps and read through Step 6 so you know how the real-robot workflow differs.
5. Safety, Ethics and Accessibility Notes
The simulation steps carry over the operational notes from Lab 05:
start_container.shruns the container with--network=host,--pid=host,--ipc=hostand GPU passthrough, and grants X-server access withxhost +local:root. Use these flags only with the FOSSBotEduSim image you built yourself. After the lab, runxhost -local:rooton the host, especially on shared machines.
Step 6 is the first time in this course that you command real hardware, so it adds physical safety:
Place the robot on the floor or on a wide table with clear space around it. A continuous
--ratepublish keeps the robot moving until you stop it, so a robot near a table edge can drive off.Learn the stop procedure before you start moving: publish a single zero-velocity
Twist(see Step 4). PressingCtrl+Con a publisher is not enough: the robot keeps executing the last command it received. In an emergency you can also callros2 service call /fossbot/estop std_srvs/srv/SetBool "{data: true}".Keep velocities small at first (
linear.xof about0.1). You can always increase them once you trust the controls.
6. Scenario and Problem Statement
In Lab 05 you watched the robot from the outside: you listed topics
and printed their messages with ros2 topic echo. That is
the subscriber half of the picture.
A robot is only useful when you can command it. Publishing is how every controller, from a joystick to a navigation planner, ultimately talks to a robot: it writes messages onto a topic that the robot’s drivers subscribe to. In this lab you will take the publisher role yourself. You will hand-craft velocity commands for the simulated FOSSBot, then send the very same commands to a real FOSSBot across the network.
7. Lab Workflow
| Phase | Student action | Expected output | Time |
|---|---|---|---|
| 1. Prepare | Start the container and launch the simulator | Gazebo + RViz2 running, /cmd_vel visible |
10 min |
| 2. Inspect the channel | Look at /cmd_vel and the Twist message
type |
Understanding of which fields move the robot | 10 min |
| 3. Publish once | Send one Twist with
ros2 topic pub --once |
The robot keeps moving until a zero Twist stops it |
15 min |
| 4. Publish continuously | Drive with ros2 topic pub --rate, then stop with a zero
Twist |
The robot drives a path and halts reliably | 20 min |
| 5. Two publishers | Run teleop and a pub on /cmd_vel at once,
inspect rqt_graph |
An understanding of what happens when publishers compete | 15 min |
| 6. Real robot | Repeat the workflow on a physical FOSSBot over Wi-Fi | The real robot moves in response to your published command | 15 min |
| 7. Reflect | Answer the analysis questions | Short written analysis | 5 min |
8. Step-by-Step Instructions
Before you start. Capture evidence as you go. The full list of artifacts you will hand in is in Section 10. Relevant tasks below are marked with a Capture for submission note.
Step 1 - Environment preparation
You will reuse the ros2_fossbot_edu Docker image that
was built in Lab 05; here you only start it. If you no longer have that
image, rebuild it from the FOSSBotEduSim repository on your workstation
before continuing:
git clone https://github.com/LRMPUT/FOSSBotEduSim.git
cd FOSSBotEduSim
bash build_image.sh- Start the container from the
FOSSBotEduSimdirectory on your workstation:
bash start_container.shWarning: If your workstation has no NVIDIA GPU, use
bash start_container_no_gpus.shinstead, exactly as in Lab 05.
- Launch the simulator from inside the container:
ros2 launch fossbot_educational_description single.launch.py world:=simple_shapes.sdfTwo windows open: a Gazebo window with the FOSSBot in a world full of boxes, and an RViz2 window. Leave both running.
- Open a second terminal attached to the same container (run this on the host):
docker exec -it ros2_fossbot_edu bashThis second shell already has ROS 2 and the FOSSBot workspace sourced
from ~/.bashrc. You will use it to inspect topics while the
simulator runs in the first terminal.
Expected result: Gazebo and RViz2 are running, and
ros2 topic list in the second terminal shows
/cmd_vel, /odom and /scan among
the topics.
Step 2 - Inspect the command channel
Before publishing, look at the topic you are about to write to and the message it carries. In the second terminal:
ros2 topic info /cmd_vel
ros2 interface show geometry_msgs/msg/Twistros2 topic info tells you the message type of
/cmd_vel is geometry_msgs/msg/Twist, and how
many publishers and subscribers it currently has. The Twist
message looks like this:
Vector3 linear
float64 x
float64 y
float64 z
Vector3 angular
float64 x
float64 y
float64 z
The FOSSBot is a differential-drive robot: two driven wheels
and a non-holonomic constraint, which is the same kind of motion model
as the turtlesim turtle. It can only drive along its own
forward axis and rotate about the vertical axis. Of the six numbers in a
Twist, only two have any effect:
linear.x- forward (positive) or backward (negative) speed, in metres per second.angular.z- turning rate (positive turns left), in radians per second.
The other four fields stay at 0.0.

ros2 topic info /cmd_vel reports the message type
and that one subscriber (the bridge) is listening, while
ros2 interface show geometry_msgs/msg/Twist reveals the two
Vector3 fields.
Tip: You do not need to memorise message type names. Type
ros2 interface show geometry_msgs/msg/Twand pressTab, and the terminal completes it for you. The same trick works for topic and package names.
Task 2.1
Looking at the Twist structure above, state which two
fields move this particular robot and why the other four are ignored.
You will use this answer in Step 3.
Expected result: You can point to
linear.x and angular.z and explain that a
differential-drive robot has only those two degrees of freedom.
Step 3 - Publish a single command
Now you take the publisher role. In the second terminal, send a
single Twist that asks the robot to move forward
slowly:
ros2 topic pub --once /cmd_vel geometry_msgs/msg/Twist "{linear: {x: 0.2}, angular: {z: 0.0}}"The --once flag publishes the message exactly one time
and then exits. Watch the Gazebo window: the robot starts driving
forward and keeps going, even though
ros2 topic pub has already finished.
The simulator’s velocity controller remembers the last command it received and keeps applying it until a new one arrives. One message is enough to set the robot moving, and nothing stops it except another message. Stop it now by publishing a zero-velocity command:
ros2 topic pub --once /cmd_vel geometry_msgs/msg/Twist "{linear: {x: 0.0}, angular: {z: 0.0}}"Warning: Keep this zero
Twistcommand at hand for the whole lab. It is the one command that reliably stops the robot, in simulation and on real hardware.
The physical FOSSBot you will use in Step 6 behaves the same way: its bridge keeps resending your last command to the robot. Many other robots, however, treat a command as valid only for a short time and stop the wheels when no new one arrives. This safety feature is called a watchdog.
To watch the effect numerically, open a third terminal
(docker exec -it ros2_fossbot_edu bash on the host) and
echo the odometry:
ros2 topic echo /odom --onceRun it before publishing the forward command, again a few seconds
after, and once more after the zero command. Compare the
position values: x grows while the robot
drives and stays put once you have stopped it.

Before publishing, the robot is at the origin (x
near 0). Shortly after a single
ros2 topic pub --once forward command, /odom
reports x of about 0.19, and the value keeps
growing until a zero Twist is published.
Task 3.1
Send a command that makes the robot rotate in place, without moving forward:
ros2 topic pub --once /cmd_vel geometry_msgs/msg/Twist "{linear: {x: 0.0}, angular: {z: 0.5}}"Confirm in Gazebo that the robot turns on the spot rather than
driving forward, then stop it with the zero Twist.
Expected result: A single forward command sets the
robot driving until you publish the zero Twist; a single
rotation command spins it in place until you stop it the same way.
Capture for submission: screenshot the
ros2 topic pubcommand together with the robot displaced in Gazebo (or the changed/odomnumbers).
Step 4 - Publish continuously and stop safely
On the FOSSBot, simulated or real, a single command is enough to keep
the robot moving, but you cannot rely on that in general: on a robot
with a watchdog the wheels stop a fraction of a second after the last
command. Real controllers, from a joystick to a navigation planner,
therefore publish repeatedly. The --rate flag does
this for you: it republishes the same message at a fixed frequency until
you stop it.
- In the second terminal, start a continuous forward command at 10 Hz:
ros2 topic pub --rate 10 /cmd_vel geometry_msgs/msg/Twist "{linear: {x: 0.2}}"The robot now drives forward with a fresh command arriving every 0.1 s. On a robot with a watchdog, this steady stream is what keeps it moving.
- In the third terminal, watch the position climb:
ros2 topic echo /odom- Stop the robot. First press
Ctrl+Cin the second terminal to end the continuous publish. The robot does not stop, in simulation or on the real FOSSBot: it keeps applying the last command it received. Publish one explicit zero-velocity command:
ros2 topic pub --once /cmd_vel geometry_msgs/msg/Twist "{linear: {x: 0.0}, angular: {z: 0.0}}"Warning: Get into the habit of sending this zero
Twistafter everyCtrl+C. On the real robot in Step 6 it is what reliably halts the hardware, and an explicit zero command stops any robot, whatever its driver does.
Task 4.1
Drive the robot in a rough square. Alternate between a few seconds of
forward motion and a short rotation, using --rate publishes
and Ctrl+C between them. Remember that the robot keeps
executing the last command after Ctrl+C, so start the next
leg promptly:
# forward leg
ros2 topic pub --rate 10 /cmd_vel geometry_msgs/msg/Twist "{linear: {x: 0.2}}"
# Ctrl+C, then turn roughly 90 degrees
ros2 topic pub --rate 10 /cmd_vel geometry_msgs/msg/Twist "{angular: {z: 0.5}}"
# Ctrl+C, repeatWatch the position and orientation fields
of /odom change as you go.
Expected result: The robot follows your commands
while you publish, and a single zero Twist reliably brings
it to a stop.
Capture for submission: screenshot the
--ratecommand beside the moving robot and the updating/odomterminal.
Step 5 - Two publishers on one topic
A topic does not belong to a single publisher. Any number of
publishers can write to /cmd_vel, and ROS 2 does not decide
which one “wins”; it simply delivers every message to the subscribers in
the order they arrive. You can see this directly.
- In the second terminal, start the keyboard teleoperator from Lab 05:
ros2 run teleop_twist_keyboard teleop_twist_keyboard- In the third terminal, start a competing continuous command:
ros2 topic pub --rate 10 /cmd_vel geometry_msgs/msg/Twist "{angular: {z: 0.5}}"Now drive with the teleop keys in the second terminal. The robot twitches and fights itself: each subscriber message is whichever command arrived most recently, so the motion alternates between your keypresses and the steady rotation.
- Open a fourth terminal and start the graph visualiser:
rqt_graphFind the /cmd_vel topic and notice that two arrows now
point into it: the teleop_twist_keyboard node and the
ros2 topic pub process are both publishers on the same
topic.

Two publishers (here two ros2 topic pub commands,
shown as the /_ros2cli_... nodes on the left) both feed the
single /cmd_vel topic, which the
/ros_gz_bridge node subscribes to. ROS 2 delivers every
message from both; it does not pick a winner.
This is why production robots place a multiplexer between
many command sources and the wheels. The twist_mux package
(already installed in the container) listens to several command topics,
applies a priority order, and forwards only the winner to
/cmd_vel, so a high-priority source such as an emergency
stop always overrides a joystick or a planner.
When you are done, stop both publishers (Ctrl+C in each
terminal) and send the zero Twist from Step 4.
Task 5.1
With both publishers running, take a screenshot of
rqt_graph showing two arrows feeding into
/cmd_vel.
Tip: If the graph looks empty or shows only one publisher, change the top-left dropdown to Nodes/Topics (all) and click the refresh icon.
Expected result: The robot responds erratically
while two publishers compete, and rqt_graph shows both of
them connected to /cmd_vel.
Capture for submission: the
rqt_graphscreenshot with two publishers on/cmd_vel.
Step 6 - Do the same on a real FOSSBot
A real FOSSBot runs its own ROS 2 nodes and exposes the same topics as the simulator. Instead of launching a simulator, you join the robot’s network and let ROS 2 discover it.
Connect to the robot by following Connecting to real robot,
then come back here. Do not launch the simulator. When
ros2 topic list shows the robot’s /cmd_vel,
/odom and /scan, you are ready for the tasks
below, which use the same commands you ran in simulation.
Task 6.1
Clear the space around the robot and have the stop command ready.
Publish the same single forward command you used in Step 3, keeping the speed
small, then stop it with the zero Twist from Step 4:
ros2 topic pub --once /cmd_vel geometry_msgs/msg/Twist "{linear: {x: 0.1}, angular: {z: 0.0}}"
# then, to be sure it is stopped:
ros2 topic pub --once /cmd_vel geometry_msgs/msg/Twist "{linear: {x: 0.0}, angular: {z: 0.0}}"The real robot behaves exactly like the simulated one: it keeps
driving until the zero Twist arrives, because the bridge
keeps resending your last command. Its watchdog only cuts the motors if
the bridge stops or the Wi-Fi link is lost for more than 0.5 s; it is a
last-resort safety net, not a way to stop the robot.
Task 6.2
In another terminal of your lab container, prepared as described in Connecting to real robot, echo the robot’s sensors and compare them with what you saw in simulation:
ros2 topic echo /odom --once
ros2 topic echo /scan --onceWarning: Clear space around the robot before every publish, and keep one hand near the keyboard to send the zero
Twist. A real robot does not stop when you stop typing or pressCtrl+C. The bridge keeps sending your last command until you publish a zeroTwist.
Expected result: The real FOSSBot moves in response
to your published Twist, and its /odom and
/scan messages have the same structure as the
simulator’s.
Capture for submission: a photo or short video of the real robot moving, with the published command visible on screen.
9. Analysis Questions
In Step 3 a single
--onceTwistset the robot moving, and it kept moving afterros2 topic pubhad exited, both in simulation and on the real FOSSBot in Step 6. Why? Many robots instead stop automatically when commands stop arriving (a watchdog). What are the advantages and risks of each approach?In Step 5 two publishers wrote to
/cmd_velat the same time. What did the robot actually do, and why does ROS 2 allow a second publisher rather than rejecting it? Give one situation where having several publishers on one command topic is genuinely useful.In Step 4 you published a zero
Twistafter pressingCtrl+C. Why does stopping the publisher not stop the robot, and why is an explicit zeroTwistthe reliable way to halt it, even on a robot that has a watchdog?In Step 6 you sent the real robot the same commands as the simulator. Did it move the same way for the same
Twist? Give two reasons why a real robot’s motion can differ from the simulated one.
10. Submission Requirements
A screenshot of a
ros2 topic pubcommand together with the robot displaced in Gazebo or the changed/odomnumbers, from Step 3 or Step 4.A screenshot of
rqt_graphshowing two publishers on/cmd_vel, from Task 5.1.A photo or short video of the real FOSSBot moving in response to your published command, with the command visible, from Task 6.1.
Short answers (2 to 3 sentences each) to the four analysis questions.
11. References and Open Licence
- ROS 2 Jazzy documentation: https://docs.ros.org/en/jazzy/
- Understanding ROS 2 topics: https://docs.ros.org/en/jazzy/Tutorials/Beginner-CLI-Tools/Understanding-ROS2-Topics/Understanding-ROS2-Topics.html
- FOSSBotEduSim repository: https://github.com/LRMPUT/FOSSBotEduSim
- Loosely based on the Publishing data lab in the
lab-tasfrscourse, Poznan University of Technology, Institute of Robotics and Machine Intelligence.
The Creative Commons Attribution 4.0 International (CC BY 4.0) license allows users to share, copy, distribute, and adapt the work, even for commercial purposes, as long as proper credit is given to the original creator.
EU funding disclaimer
Funded by the European Union. Views and opinions expressed are however those of the author(s) only and do not necessarily reflect those of the European Union or the European Education and Culture Executive Agency (EACEA). Neither the European Union nor EACEA can be held responsible for them.